package server

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"gc-rds-ha/internal/common"
	msg "gc-rds-ha/scripts"
	"net"
	"os"
	"time"

	"google.golang.org/grpc/credentials"

	"google.golang.org/grpc/reflection"

	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
)

const (
	defaultMaxRecvMsgSize = 1024*1024*1000 + 12
	defaultMaxSendMsgSize = 1024*1024*1000 + 12
)

var (
	// 避免恶意client 的参数
	kaep = keepalive.EnforcementPolicy{
		MinTime:             5 * time.Second, // 如果client 间隔<=5s就ping一次，就关闭连接. 目前go grpc client 10s ping 一次
		PermitWithoutStream: true,            // 允许无活动的连接发ping
	}
	// keepalive 常规参数
	kasp = keepalive.ServerParameters{
		MaxConnectionIdle: 5 * time.Minute, // 空闲超过5分钟 关闭
	}
)

func withRPCProtocol() SetOption {
	return func(rs *Option) error {
		rs.Protocol = GRPC
		return nil
	}
}

func (rs *RPCServer) initRpcServer() error {
	if err := rs.checkOption(); err != nil {
		return err
	}
	serverOptions := make([]grpc.ServerOption, 0)
	serverOptions = append(serverOptions,
		grpc.KeepaliveEnforcementPolicy(kaep),
		grpc.KeepaliveParams(kasp),
		grpc.MaxRecvMsgSize(defaultMaxRecvMsgSize),
		grpc.MaxSendMsgSize(defaultMaxSendMsgSize),
		grpc.UnaryInterceptor(rs.unaryServerInterceptor),
		grpc.StreamInterceptor(rs.streamServerInterceptor),
	)
	if common.AgentConfig.EnableTLS {
		serverOptions = append(serverOptions, withTLS())
	}
	rs.gServer = grpc.NewServer(serverOptions...)
	reflection.Register(rs.gServer)
	return nil
}

func withTLS() grpc.ServerOption {
	// 进行加载 key pair ,读取和解析公钥-私钥对并创建 X.509 密钥对
	cert, err := tls.LoadX509KeyPair(common.AgentConfig.GcRdsHa.ServerPem, common.AgentConfig.GcRdsHa.ServerKey)
	if err != nil {
		log.Println("加载 x509 证书失败!\n", err)
	}
	// 通过 CA 创建证书池
	certPool := x509.NewCertPool()

	// 向证书池中加入证书
	CAFileBytes, err := os.ReadFile(common.AgentConfig.GcRdsHa.Crt)
	if err != nil {
		log.Println("读取 ca.crt 证书失败!\n", err)
	}
	// 加载客户端证书
	//certPool.AddCert()

	// 加载证书从 pem 文件里面，将来自 CA 的客户端证书附加到证书池中
	certPool.AppendCertsFromPEM(CAFileBytes)

	// 创建 credentials 对象，通过创建 TLS 凭证为所以传入的连接启用 TLS
	creds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{cert},        // 服务端证书
		ClientAuth:   tls.RequireAndVerifyClientCert, // 需要并且验证客户端证书
		ClientCAs:    certPool,                       // 客户端证书池
	})
	return grpc.Creds(creds)
}

// RPCServer Rpc 服务
type RPCServer struct {
	option  *Option
	gServer *grpc.Server
}

func (rs *RPCServer) checkOption() error {
	port := rs.option.Port
	if port < 1024 {
		return fmt.Errorf("bad Port %d", port)
	}
	return nil
}

func (rs *RPCServer) unaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
	// TODO: log TimeCost? Auth? Metadata?
	startTime := time.Now()

	resp, err = handler(ctx, req)

	log.WithFields(log.Fields{
		"ServerName": rs.option.Name,
		"FullMethod": info.FullMethod,
	}).Infof("TimeCost: %s", time.Since(startTime))
	return
}

func (rs *RPCServer) streamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
	// TODO: log what?
	return handler(srv, ss)
}

func (rs *RPCServer) String() string {
	j, _ := json.Marshal(rs.option)
	return "RPCServer:" + string(j)
}

func (rs *RPCServer) GetServer() *grpc.Server {
	return rs.gServer
}

func (server *GcRdsHaServer) Start() {

	go server.grpcRun()
	go server.HttpRun()

	<-server.serverStart

}

func (server *GcRdsHaServer) grpcRun() {
	msg.RegisterGcRdsHaServer(server.RpcServer.gServer, server)
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", server.RpcServer.option.Listen, server.RpcServer.option.Port))
	if err != nil {
		log.Errorf("listen failed! %s", server.RpcServer.String())
	}
	log.Infof("Server started, %v", server.RpcServer.String())
	if err = server.RpcServer.gServer.Serve(listener); err != nil {
		log.Errorf("Server stopped %v, %v", err, server.RpcServer.String())
	}
}

func (server *GcRdsHaServer) Stop(graceful bool) {
	if graceful {
		server.RpcServer.gServer.GracefulStop()
	}
	if !graceful {
		server.RpcServer.gServer.Stop()
	}

	// todo: 手动关闭http连接
	server.serverStart <- struct{}{}
}
